#include "from_xml.h"

#include <QDomElement>
#include <QString>
#include <QStringList>

#include "roles.h"
#include "rows.h"
#include "columns.h"
#include "model_class.h"


namespace BitWin
{


// Anonymous namespace for internal usage staff
namespace
{


// Xml document tags and attributes names
namespace XmlNames
{
    const QString
        ROWS("rows"), COLS("columns"),
        WORD("word"), FIELD("field"),
        HEX("hex"), DEC("dec"), BIN("bin"),
        TITLE("title"), RANGE("bits"),
        RANGE_SEP(":")
    ;
};


// Convert a string in form "a:b" to the BitRange(a, b)
BitRange str2range(const QString& s)
{
    QStringList list(
        s.split(XmlNames::RANGE_SEP, QString::SkipEmptyParts)
    );
    
    if (list.size() == 1)
    {
        bool conv_ok = false;
        unsigned val = list[0].toUInt(&conv_ok, 10);
        if (conv_ok)
            return BitRange(val, val);
    }
    else if (list.size() == 2)
    {
        bool fst_ok(false), last_ok(false);
        unsigned fst = list[0].toUInt(&fst_ok, 10);
        unsigned last = list[1].toUInt(&last_ok, 10);
        if (fst_ok && last_ok)
            return BitRange(fst, last);
    }
    
    return BitRange();
}

PRowList procWordElement(const QDomElement& element, unsigned* word_no)
{
    PRowList result;
    
    result << PRow(
        new WordRow(element.attribute(XmlNames::TITLE), *word_no)
    );
    
    for (
        QDomElement field_element =
            element.firstChildElement(XmlNames::FIELD)
        ;
        !field_element.isNull();
        field_element =
            field_element.nextSiblingElement(XmlNames::FIELD)
    )
        result << PRow(
            new FieldRow(
                field_element.attribute(XmlNames::TITLE),
                str2range(field_element.attribute(XmlNames::RANGE)),
                *word_no
            )
        );
    
    ++*word_no;
    
    return result;
}


PColumnList procColumnsElement(const QDomElement& element)
{
    PColumnList result;
    
    for (
        QDomElement column_element =
            element.firstChildElement()
        ;
        !column_element.isNull();
        column_element = column_element.nextSiblingElement()
    )
    {
        QString tag_name = column_element.tagName();
        QString title = column_element.attribute(XmlNames::TITLE);
        
        if (XmlNames::BIN == tag_name)
            result << PColumn(
                new BitsColumn(
                    title,
                    str2range(column_element.attribute(XmlNames::RANGE))
                )
            );
        else if (XmlNames::HEX == tag_name)
            result << PColumn(new DisplayColumn(title, HexDataRole));
        else if (XmlNames::DEC == tag_name)
            result << PColumn(new DisplayColumn(title, DecDataRole));
    }
    
    return result;
}


} // Anonymous namespace end


PRowList FromXml::makeRows(const QDomElement& element)
{
    PRowList result;
    unsigned word_no = 0;
    
    for (
        QDomElement rows_element =
            element.firstChildElement(XmlNames::ROWS)
        ;
        !rows_element.isNull();
        rows_element = rows_element.nextSiblingElement(XmlNames::ROWS)
    )
    {
        for (
            QDomElement word_element =
                rows_element.firstChildElement(XmlNames::WORD)
            ;
            !word_element.isNull();
            word_element =
                word_element.nextSiblingElement(XmlNames::WORD)
        )
            result << procWordElement(word_element, &word_no);
    }
    
    return result;
}

PColumnList FromXml::makeColumns(const QDomElement& element)
{
    PColumnList result;
    
    for (
        QDomElement columns_element =
            element.firstChildElement(XmlNames::COLS)
        ;
        !columns_element.isNull();
        columns_element =
            columns_element.nextSiblingElement(XmlNames::COLS)
    )
        result << procColumnsElement(columns_element);
    
    return result;
}

Model* FromXml::makeModel(const QDomElement& element)
{
    return new Model(
        makeRows(element),
        makeColumns(element)
    );
}


} // BitWin namespace end
